/* ccscript parser */

#include "parser.h"

#include "lexer.h"
#include "ast.h"


using std::string;
using std::stringstream;

//
// Constructs a parser for the given source string
//
Parser::Parser(const std::string& src)
	: error(NULL), line(0)
{
	this->lexer = new Lexer(src);
	this->scope = new Parser::SymbolTable();

	init();
	advance();
}

void Parser::SetErrorHandler(ErrorReceiver* e)
{
	this->error = e;
	this->lexer->SetErrorHandler(e);
}

ErrorReceiver* Parser::GetErrorHandler() const
{
	return this->error;
}

Program* Parser::Parse()
{
	//return program();
	return NULL;
}

Expression* Parser::ParseExpression()
{
	return expression(0);
}


/*
 * Private methods
 */

void Parser::Error(const string &msg, int line)
{
	if(error) error->Error(msg, line, -1);
}

void Parser::Warning(const string &msg, int line)
{
	if(error) error->Warning(msg, line, -1);
}


//
// Beginning of new expression parsing implementation.
// Based on Vaughan Pratt's "Top Down Operator Precedence," neatly
// adapted to JavaScript by Douglas Crockford here:
//   http://javascript.crockford.com/tdop/tdop.html
//



/*
 * A token type augmented to support parsing methods as per the
 * approach given by Pratt's "Top Down Operator Precedence"
 */

void ParseSymbol::Error(const string& msg) const
{
	if(parser)
		parser->Error(msg, line);
}

//
// Attempting any parse denotation on a basic symbol is an error
//
Expression* ParseSymbol::nud()
{
	Error("expected an expression, but found '" + value + "'"); 
	return new ErrorExpr(line, "expected expression, found '"+value+"'",
		parser->GetErrorHandler());
}

Expression* ParseSymbol::led(Expression* left)
{
	Error("invalid operator '" + value + "'");
	return new ErrorExpr(line, "invalid operator '"+value+"'",
		parser->GetErrorHandler());
}

Statement* ParseSymbol::std()
{
	Error("expected a statement, but found '" + value + "'");
	return new ExprStmt(line, new ErrorExpr(line, "expected a statement, but found '" + value + "'",
		parser->GetErrorHandler()), parser->GetErrorHandler());
}



struct IdentSymbol : public ParseSymbol
{
	IdentSymbol(const string& name, Parser* parser)
		: ParseSymbol(name, 0, "name", parser)
	{ has_nud = true; }
	ParseSymbol* clone() const {
		return new IdentSymbol(value, parser);
	}

	Expression* nud() {
		// return a primary expression referring to the identifier

		// NOTE:
		// IdentExpr will be changed to remove the hardcoded "module.ident" stuff,
		// so the second parameter will go away
		return new IdentExpr(line, "", value, parser->GetErrorHandler());
	}
};


struct LiteralSymbol : public ParseSymbol
{
	LiteralSymbol(const string& value, Parser* parser)
		: ParseSymbol(value, 0, "literal", parser)
	{ has_nud = true; }
	ParseSymbol* clone() const {
		return new LiteralSymbol(value, parser);
	}

	string	type;		// the 
	int		ival;		// integer value, if type == "number"
	float	fval;		// float value, if type == "float" (NOTE: float not yet supported)
	bool	bval;		// boolean value, if type == "boolean" (NOTE: bool not yet supported)

	Expression* nud() {
		if(type == "string")
			return new StringLiteral(line, value, parser->GetErrorHandler());
		else if(type == "number")
			return new IntLiteral(line, ival, parser->GetErrorHandler());

		return NULL;	// TODO: throw error too, this is bad mojo
	}
};


//
// Left-associative infix operator
//
struct InfixSymbol : public ParseSymbol
{
	InfixSymbol(const string& value, int lbp, Parser* parser)
		: ParseSymbol(value, lbp, "binary", parser)
	{ has_led = true; }
	ParseSymbol* clone() const {
		return new InfixSymbol(value, lbp, parser);
	}

	Expression* led(Expression* left) {
		BinaryExpr* exp = new BinaryExpr(line, parser->GetErrorHandler());
		exp->op = value;
		exp->left = left;
		exp->right = parser->expression(lbp);
		return exp;
	}
};


//
// Right-associative infix operator
//
struct InfixRightSymbol : public ParseSymbol
{
	InfixRightSymbol(const string& value, int lbp, Parser* parser)
		: ParseSymbol(value, lbp, "binary", parser)
	{ has_led = true; }
	ParseSymbol* clone() const {
		return new InfixRightSymbol(value, lbp, parser);
	}
	Expression* led(Expression* left) {
		BinaryExpr* exp = new BinaryExpr(line, parser->GetErrorHandler());
		exp->op = value;
		exp->left = left;
		// Use a smaller left binding power; this forces similar operators
		// to associate to the right instead of the left.
		exp->right = parser->expression(lbp - 1);
		return exp;
	}
};


//
// '[' infix operator for subscripts
//
struct SubscriptSymbol : public InfixSymbol
{
	SubscriptSymbol(Parser* parser)
		: InfixSymbol("[", 80, parser) { has_led = true; }
	ParseSymbol* clone() const { 
		return new SubscriptSymbol(parser);
	}
	Expression* led(Expression* left) {
		Expression* key = parser->expression(0);
		parser->advance("]", "malformed expression or missing closing bracket?");
		return new SubscriptExpr(line, left, key, parser->GetErrorHandler());
	}
};


//
// Prefix operators
//
struct PrefixSymbol : public ParseSymbol
{
	PrefixSymbol(const string& value, Parser* parser)
		: ParseSymbol(value, 70, "unary", parser) { has_nud = true; }
	ParseSymbol* clone() const {
		return new PrefixSymbol(value, parser);
	}
	Expression* nud() {
		UnaryExpr* exp = new UnaryExpr(line, parser->GetErrorHandler());
		exp->op = value;
		exp->expr = parser->expression(lbp);
		return exp;
	}
};


//
// '(' operator, both prefix and infix
//
struct ParenSymbol : public ParseSymbol
{
	ParenSymbol(Parser* parser)
		: ParseSymbol("(", 80, "operator", parser) { has_nud = true; has_led = true; }
	ParseSymbol* clone() const {
		return new ParenSymbol(parser);
	}
	Expression* nud() {
		Expression* expr = parser->expression(0);
		parser->advance(")", "closing parenthesis missing?");
		return expr;
	}
	Expression* led(Expression* left)
	{
		FunctionCallExpr* call = new FunctionCallExpr(line, parser->GetErrorHandler());

		call->func_expr = left;

		// Push arguments
		if(parser->token->value != ")")
		{
			while (true)  {
				// Provide an informative message in cases like "foo(x, y, )"
				if( parser->token->value == ")") {
					parser->Error("extra comma or missing argument in call to " + left->ToString(0), line);
					break;
				}

				call->PushArg( parser->expression(0) );
                if( parser->token->value != ",")
					break;

				parser->advance(",", "amazing disappearing comma?");
            }
		}
		parser->advance(")", "argument list missing closing parenthesis");

		return call;
	}
};

//
// '{' operator, for object expressions and block statements
//
struct BraceSymbol : public ParseSymbol
{
	BraceSymbol(Parser* parser)
		: ParseSymbol("{", 80, "operator", parser) { has_nud = true; has_std = true; }
	ParseSymbol* clone() const {
		return new BraceSymbol(parser);
	}

	Expression* nud() {
		ObjectLiteralExpr* obj = new ObjectLiteralExpr(line, parser->GetErrorHandler());
		if(parser->token->value != "}") {
			while(true) {
				ParseSymbol::Ptr id = parser->token;
				if(id->arity != "name" && id->arity != "literal") {
					Error("bad property name: must be identifier or literal value");
				}
				parser->advance();
				parser->advance(":", "property name must be followed by ':'");

				Expression* key = id->nud();
				Expression* value = parser->expression(0);
				obj->AddProperty(key, value);
				
				if(parser->token->value != ",")
					break;
				parser->advance(",", "missing comma after property");
			}
		}
		parser->advance("}", "object literal missing closing brace");
		return obj;
	}

	Statement* std() {
		parser->push_scope();
		Block* block = new Block(line, parser->GetErrorHandler());
		while(true) {
			if(parser->token->value == "}" || parser->token->value == "(end)")
				break;
			block->Add( parser->statement() );
		}
		parser->advance("}", "missing closing brace");
		parser->pop_scope();
		return block;
	}
};

//
// Selection operator
//
struct SelectionSymbol : public InfixSymbol
{
	SelectionSymbol(const string& value, Parser* parser)
		: InfixSymbol(value, 80, parser) {  }
	ParseSymbol* clone() const {
		return new SelectionSymbol(value, parser);
	}
	Expression* led(Expression* left) {
		SelectionExpr* exp = new SelectionExpr(line, parser->GetErrorHandler());
		exp->left = left;

		// The following symbol must be an identifier
		if(parser->token->arity != "name")
			Error("expected a property name, found token '" + parser->token->value + "'");

		exp->right = parser->token->nud();
		parser->advance();
		return exp;
	}
};



void Parser::init()
{
	//
	// Define the default operators and other parsing symbols
	//
	// What we're actually doing is populating "scope" with
	// ParseSymbol objects that will be _cloned_ when a matching
	// token comes up. So essentially we're using prototypes. Not
	// entirely surprising given that this new parsing approach
	// is the result of porting a JavaScript parser. :-)
	//

	// Non-binding symbols
	scope->define_op( new LiteralSymbol("(literal)", this) );
	scope->define_op( new ParseSymbol("(end)", this) );
	scope->define_op( new ParseSymbol(":", this) );
	scope->define_op( new ParseSymbol(";", this) );
	scope->define_op( new ParseSymbol(")", this) );
	scope->define_op( new ParseSymbol("]", this) );
	scope->define_op( new ParseSymbol("}", this) );
	scope->define_op( new ParseSymbol(",", this) );
	scope->define_op( new ParseSymbol("else", this) );

	// Assignment operators
	scope->define_op( new InfixRightSymbol("=", 10, this) );
	scope->define_op( new InfixRightSymbol("+=", 10, this) );
	scope->define_op( new InfixRightSymbol("-=", 10, this) );
	scope->define_op( new InfixRightSymbol("*=", 10, this) );
	scope->define_op( new InfixRightSymbol("/=", 10, this) );

	// Logical operators
	scope->define_op( new InfixRightSymbol("&&", 30, this) );
	scope->define_op( new InfixRightSymbol("||", 30, this) );

	// Relational operators
	scope->define_op( new InfixRightSymbol("==", 40, this) );
	scope->define_op( new InfixRightSymbol("!=", 40, this) );

	// Arithmetic operations
	scope->define_op( new InfixSymbol("+", 50, this) );
	scope->define_op( new InfixSymbol("-", 50, this) );
	scope->define_op( new InfixSymbol("*", 60, this) );
	scope->define_op( new InfixSymbol("/", 60, this) );
	scope->define_op( new InfixSymbol("%", 60, this) );
	
	// Prefix operators
	scope->define_op( new PrefixSymbol("!", this) );
	scope->define_op( new PrefixSymbol("-", this) );

	// "Enclosing" infix operators
	scope->define_op( new ParenSymbol(this) );			// "()" expression grouping and function call
	scope->define_op( new SubscriptSymbol(this) );		// "[]" subscripting
	scope->define_op( new BraceSymbol(this) );			// "{}" blocks and object literals

	// Member selection
	scope->define_op( new SelectionSymbol(".", this) );

	// Crap
	scope->define_op( new InfixSymbol("and", 20, this) );
}


//
// Parses a single statement
//
Statement* Parser::statement()
{
	ParseSymbol::Ptr t = token;
	if(t->has_std) {
		advance();
		return t->std();
	}
	ExprStmt* e = new ExprStmt(token->line, expression(0),
		GetErrorHandler());
	// Semicolons are optional
	if(token->value == ";")
		advance();
	return e;
}

//
// Parses statements until a closing brace or end-of-file is reached
//
Statement* Parser::statements()
{
	return NULL;
}


//
// Parses an expression.
// Parameters:
//  rbp		The "right binding power," controlling the relative
//			level of aggressiveness in binding tokens to the right
//
Expression* Parser::expression(int rbp)
{
	Expression* left = NULL;

	// Get next token
	// token = Advance();

	ParseSymbol::Ptr t = token;
	advance();

	if(!t) {
		// TODO: throw exception here also? Or just report error?
		// Actually failing to find a token is pretty bad mojo.
		return NULL;
	}

	if(t->value == "(end)")
		Error("unexpected end of file in expression", t->line);

	// Begin with the null denotation of the token
	left = t->nud();

	// Apply the left denotation until the right binding power
	// matches or exceeds the left binding power of the token
	while(rbp < token->lbp)
	{
		t = token;

		// Because we don't use semicolons to separate statements,
		// we might run into a valid sequence of expression statements
		// like "a !b" that might confuse the parser. Because prefix
		// operators have very high binding power, we would ordinarily
		// try to interpret the above sequence as (a ! b), and call
		// the ! symbol's left denotation function, which is erroneous.
		// Here we anticipate that circumstance whenever the next operator
		// has no left denotation, and simply end the expression early.
		if(!t->has_led)
			break;

		advance();
		left = t->led(left);
	}

	return left;
}



//
// Parser symbol table methods
//
Parser::SymbolTable::SymbolTable()
	: parent(NULL)
{
}

ParseSymbol* Parser::SymbolTable::find(const std::string& name) const
{
	std::map<string,ParseSymbol*>::const_iterator found;
	found = table.find(name);

	if(found != table.end())
		return found->second;
	else if(parent)
		return parent->find(name);
	else
		return NULL;
}

void Parser::SymbolTable::define_op(ParseSymbol* sym)
{
	if(find(sym->value)) {
		// We really should signal an error here somehow...
		// Actually, throwing an exception would be preferable,
		// since multiple definitions are a pretty exceptional
		// condition -- it shouldn't happen even when parsing
		// syntactically invalid code.
		return;
	}
	table[sym->value] = sym;
}


void Parser::push_scope()
{
	SymbolTable* s = new SymbolTable();
	s->parent = scope;
	scope = s;
}

void Parser::pop_scope()
{
	SymbolTable* popped = scope;
	if(!popped)
		return;	// TODO: signal error?
	scope = scope->parent;
	delete popped;
}

ParseSymbol::Ptr Parser::advance()
{
	string value, arity;

	// Get the next token from the lexer
	lexer->Lex();
	Token last = lexer->GetCurrentToken();

	value = last.value;
	arity = last.type;
	
	// Now we need to figure out what kind of parsing symbol
	// to create for this token.
	const ParseSymbol* o = NULL;

	if(last.type == "name") {
		o = scope->find(last.value);

		if(!o) {
			// the token is just a plain ol' identifier.
			// create a new IdentifierSymbol:
			IdentSymbol* id = new IdentSymbol(last.value, this);
			id->line = last.line;

			token.reset(id);
			return token;
		}
	}
	else if(last.type == "operator") {
		// we actually treat operators and names similarly here
		o = scope->find(last.value);
		// except if we don't find an operator symbol we abort:
		if(!o) {
			Error("unknown operator " + last.value, last.line);
			token.reset();
			return token;
		}
	}
	else if(last.type == "string" || last.type == "number") {
		// Always use a LiteralSymbol for literals
		LiteralSymbol* lit = new LiteralSymbol(last.value, this);
		lit->line = line;
		lit->type = last.type;
		// special processing for non-string literals
		if(lit->type == "number")
			lit->ival = last.ival;

		token.reset(lit);
		return token;
	}
	else if(last.type == "(end)") {
		o = scope->find("(end)");
	}
	else {
		Error("unknown token", last.line);
	}

	if(!o) {
		Error("token type not found in scope", last.line);
		token.reset();
		return token;
	}
		

	// Next we create a clone of the returned symbol
	token.reset( o->clone() );

	// the cloned token should already have a more appropriate arity;
	// the arity obtained from last.type is likely to be too vague,
	// e.g., "operator"
	//token->arity = arity;
	token->value = last.value;
	token->line = last.line;

	

	return token;
}


//
// Processes the next token from the lexer, creating and returning a
// ParseSymbol that can be used to parse the token. Optionally checks
// against an expected token value, reporting an error if the last
// token did not have that value.
//
ParseSymbol::Ptr Parser::advance(const string& expected, const string& message)
{
	if(token->value != expected)
		Error("expected '" + expected + "', found '" + token->value + "': " + message, token->line);
	return advance();
}
